package com.kusej.dbdocexport;

import com.intellij.database.access.ConnectionProvider;
import com.intellij.database.dataSource.DatabaseConnection;
import com.intellij.database.dataSource.LocalDataSource;
import com.intellij.database.dataSource.connection.DGDepartment;
import com.intellij.database.model.RawConnectionConfig;
import com.intellij.database.psi.DbDataSource;
import com.intellij.database.psi.DbNamespace;
import com.intellij.database.psi.DbTable;
import com.intellij.database.remote.jdbc.RemoteConnection;
import com.intellij.database.remote.jdbc.RemotePreparedStatement;
import com.intellij.database.remote.jdbc.RemoteResultSet;
import com.intellij.database.remote.jdbc.RemoteStatement;
import com.intellij.icons.AllIcons;
import com.intellij.openapi.actionSystem.AnAction;
import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.actionSystem.LangDataKeys;
import com.intellij.openapi.ui.Messages;
import com.intellij.openapi.vfs.LocalFileSystem;
import com.intellij.psi.PsiElement;
import com.intellij.sql.psi.SqlFile;
import com.kusej.dbdocexport.model.DatabaseInfo;
import com.kusej.dbdocexport.model.TableColumnInfo;
import com.kusej.dbdocexport.model.TableIndexInfo;
import com.kusej.dbdocexport.model.TableInfo;
import com.kusej.dbdocexport.utils.DBExecutorUtils;
import com.kusej.dbdocexport.utils.FileUtils;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.compress.utils.Lists;
import org.jetbrains.annotations.NotNull;

import java.nio.file.Path;
import java.nio.file.Paths;
import java.sql.*;
import java.util.*;
import java.util.stream.Collectors;

public class DbDocExportMarkdown extends AnAction {
    @Override
    public void update(@NotNull AnActionEvent e) {
        // 根据特定条件判断是否禁用 Action
        PsiElement[] psiElements = e.getData(LangDataKeys.PSI_ELEMENT_ARRAY);
        if (psiElements == null) {
            e.getPresentation().setEnabled(false);
            return;
        }
        for (PsiElement psiElement : psiElements) {
            if (psiElement instanceof DbTable) {
                e.getPresentation().setEnabled(false);
                return;
            }
        }
        // 设置 Action 的状态
        e.getPresentation().setEnabled(true);
    }

    @Override
    public void actionPerformed(AnActionEvent e) {
        try {
            PsiElement[] psiElements = Objects.requireNonNull(e.getData(LangDataKeys.PSI_ELEMENT_ARRAY), "您未选择任何connection或db实例");
            Set<DbDataSource> dbList = new HashSet<>();
            List<DbNamespace> dbInstanceList = new ArrayList<>();
            for (PsiElement psiElement : psiElements) {
                if (psiElement instanceof DbDataSource) {
                    dbList.add((DbDataSource) psiElement);
                } else if (psiElement instanceof DbNamespace) {
                    dbInstanceList.add((DbNamespace) psiElement);
                }
            }
            if (dbList.isEmpty() && dbInstanceList.isEmpty()) {
                Messages.showErrorDialog("Message：您未选择任何connection或db实例", "ERROR");
                return;
            }
            List<String> schemaNames = Lists.newArrayList();
            if (!dbInstanceList.isEmpty()) {
                for (DbNamespace dbNamespace : dbInstanceList) {
                    schemaNames.add(dbNamespace.getName());
                    dbList.add(dbNamespace.getDataSource());
                }
            }
            if (dbList.size() > 1) {
                Messages.showErrorDialog("Message：暂不支持多个connection同时导出！", "ERROR");
                return;
            }
            DbDataSource dbDataSource = dbList.stream().findAny().get();// 数据库链接信息
            if (!Objects.requireNonNull(dbDataSource.getConnectionConfig()).getDriverClass().contains("mysql")){
                Messages.showErrorDialog("Message：目前仅支持MySQL数据库导出！", "ERROR");
                return;
            }
            exportMarkDown(dbDataSource, schemaNames);
            if (CollectionUtils.isNotEmpty(schemaNames)) {
                // 指定数据库实例导出
                Messages.showMessageDialog(String.format("Success：已导出%s实例到当前项目根目录", Arrays.toString(schemaNames.toArray())), "Message", AllIcons.Actions.Commit);
            } else {
                // 导出全部数据库实例
                Messages.showMessageDialog(String.format("Success：已导出【%s】下全部实例到当前项目根目录", dbDataSource.getName()), "Message", AllIcons.Actions.Commit);
            }
        } catch (Exception ex) {
            ex.printStackTrace();
            Messages.showErrorDialog(ex.getMessage(), "ERROR");
        }finally {
            LocalFileSystem.getInstance().refresh(true);
        }

    }

    private void exportMarkDown(DbDataSource dbDataSource, List<String> schemaNames) {
        try {
            if (CollectionUtils.isEmpty(schemaNames)) {
                schemaNames = DBExecutorUtils.getSchemas(dbDataSource);
            }
            for (String dbSchema : schemaNames
            ) {
                List<TableInfo> tableInfo = DBExecutorUtils.combineAllInfo(dbSchema,dbDataSource);
                DatabaseInfo databaseInfo = new DatabaseInfo();
                databaseInfo.setSchema(dbSchema);
                String markdownTableSeparator = " | ";
                String template0 = FileUtils.readStreamFromResource("template_0.md");
                String template1 = FileUtils.readStreamFromResource("template_1.md");
                String template2 = FileUtils.readStreamFromResource("template_2.md");
                String template3 = FileUtils.readStreamFromResource("template_3.md");
                StringBuilder resultMd = new StringBuilder(template0);
                for (TableInfo info : tableInfo) {
                    String tableName = info.getTableName();
                    String row = markdownTableSeparator + info.getIndex() + markdownTableSeparator +
                            "[" + tableName + "](#" + tableName + ")" + markdownTableSeparator +
                            info.getTableComment() + markdownTableSeparator;
                    resultMd.append(row.trim()).append("\n");
                }
                resultMd.append(template1);
                for (TableInfo info : tableInfo) {
                    String tableName = info.getTableName();
                    resultMd.append(template2.replace("{{tableName}}", tableName).replace("{{tableComment}}", info.getTableComment()));
                    for (TableColumnInfo tableColumnInfo : info.getColumnInfoList()) {
                        String row = markdownTableSeparator + tableColumnInfo.getIndex() + markdownTableSeparator +
                                tableColumnInfo.getColumnName() + markdownTableSeparator +
                                tableColumnInfo.getColumnType() + markdownTableSeparator +
                                tableColumnInfo.getColumnDefaultValue() + markdownTableSeparator +
                                tableColumnInfo.getIsNullableStr() + markdownTableSeparator +
                                tableColumnInfo.getColumnComment().replace("|", "\\|").replaceAll("\\r\\n", " ") + markdownTableSeparator;
                        resultMd.append(row.trim()).append("\n");
                    }
                    resultMd.append(template3);
                    for (TableIndexInfo tableIndexInfo : info.getIndexInfoList()) {
                        String row = markdownTableSeparator + tableIndexInfo.getIndex() + markdownTableSeparator +
                                tableIndexInfo.getIndexName() + markdownTableSeparator +
                                tableIndexInfo.getIsUniqueStr() + markdownTableSeparator +
                                tableIndexInfo.getRelatedColumn() + markdownTableSeparator +
                                tableIndexInfo.getIndexComment() + markdownTableSeparator;
                        resultMd.append(row.trim()).append("\n");
                    }
                }
                Path path = Paths.get(Objects.requireNonNull(dbDataSource.getProject().getBasePath()), dbSchema + "_数据库表.md");
                FileUtils.saveString(path, resultMd.toString());
                System.out.println("完成了 " + dbSchema + " 导出到 " + path);
            }


        } catch (Exception ex) {
            ex.printStackTrace();
            throw new RuntimeException(ex);
        }
    }


}
